﻿#region Usings

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml.XPath;
using Yandex.Market.Api.Common;

#endregion

namespace Yandex.Market.Api.Model
{
    [Serializable]
    internal class DummyOffer : IOffer, ICloneable
    {
        internal DummyOffer()
        {
            CategoryIds = new List<CategoryID>();
        }

        #region ICloneable Members

        /// <summary>
        ///   Creates a new object that is a copy of the current instance.
        /// </summary>
        /// <returns>
        ///   A new object that is a copy of this instance.
        /// </returns>
        public object Clone()
        {
            object clone;
            using(MemoryStream stream = new MemoryStream())
            {
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(stream, this);
                stream.Seek(0, SeekOrigin.Begin);
                clone = formatter.Deserialize(stream);
            }
            return clone;
        }

        #endregion

        #region IOffer Members

        /// <summary>
        ///   ID товара. Обязательный элемент.
        /// </summary>
        public uint ID { get; set; }

        ///<summary>
        ///  Статус доступности товара - в наличии/на заказ<br />
        ///  available="false" - товарное предложение на заказ. <br />
        ///  Магазин готов осуществить поставку товара на указанных условиях в течение месяца <br />
        ///  (срок может быть больше для товаров, которые всеми участниками рынка поставляются только на заказ). <br />
        ///  Те товарные предложения, на которые заказы не принимаются, не должны выгружаться в Яндекс.Маркет.<br /><br />
        ///  available="true" - товарное предложение в наличии. <br />
        ///  Магазин готов сразу договариваться с покупателем о доставке товара
        ///</summary>
        public bool? Available { get; set; }

        /// <summary>
        ///   bid (oсновная ставка) — действует на всех местах размещения с возможностью управления ценой клика, если не указана ставка для отдельного места<br />
        ///   Должен иметь целые положительные значения. Например: 21, что соответствует ставке 0,21 у.е.
        /// </summary>
        public uint? BID { get; set; }

        /// <summary>
        ///   cbid (cтавка для карточек) — действует только на карточках моделей.<br />
        ///   Должен иметь целые положительные значения. Например: 21, что соответствует ставке 0,21 у.е.
        /// </summary>
        public uint? CBID { get; set; }

        /// <summary>
        ///   Обязательный элемент.<br />
        ///   Цена, по которой данный товар можно приобрести.Цена товарного предложения округляеся и выводится в зависимости от настроек пользователя.
        /// </summary>
        public double Price { get; set; }

        /// <summary>
        /// </summary>
        public double? Wprice { get; set; }

        /// <summary>
        ///   Обязательный элемент.<br /> 
        ///   Идентификатор валюты товара (RUR, USD, UAH, KZT).<br /> 
        ///   Для корректного отображения цены в национальной валюте, необходимо использовать идентификатор (например, UAH) с соответствующим значением цены.<br />
        /// </summary>
        public YmlCurrencyId CurrencyId { get; set; }

        /// <summary>
        ///   Обязательный элемент.<br />
        ///   Идентификатор категории товара (целое число не более 18 знаков).<br /> 
        ///   Товарное предложение может принадлежать только одной категории
        /// </summary>
        public List<CategoryID> CategoryIds { get; set; }

        /// <summary>
        /// </summary>
        public string Xcategory { get; set; }

        /// <summary>
        ///   URL-адрес страницы товара. Максимальная длина URL — 255 символов.
        /// </summary>
        public string Url { get; set; }

        /// <summary>
        /// </summary>
        public string BuyUrl { get; set; }

        /// <summary>
        ///   Ссылка на картинку соответствующего товарного предложения. <br />
        ///   Недопустимо давать ссылку на "заглушку", т.е. на картинку где написано "картинка отсутствует" или на логотип магазина<br />
        ///   По указанному URL-адресу должна находиться картинка формата jpeg, gif или png размером не меньше 100х100 пикселов. <br />
        ///   Картинки большего размера будут уменьшены автоматически. <br />
        ///   Ссылка на HTML-страницу, содержащую картинку, не допустима.
        /// </summary>
        public string Picture { get; set; }

        ///<summary>
        ///  Элемент, обозначающий возможность доставить соответствующий товар. "false" данный товар не может быть доставлен ("самовывоз").<br />
        ///  "true" товар доставляется на условиях, которые указываются в партнерском интерфейсе http://partner.market.yandex.ru на странице "редактирование".
        ///</summary>
        public bool? Delivery { get; set; }

        /// <summary>
        /// </summary>
        public bool? DeliveryIncluded { get; set; }

        /// <summary>
        ///   Наличие точки продаж, где товар можно купить без предварительного заказа, указывается значением true у элемента store. <br />
        ///   Если точки продаж нет, используется значение false.
        /// </summary>
        public bool? Store { get; set; }

        /// <summary>
        ///   Если предусмотрена возможность предварительно заказать данный товар и забрать его в точке продаж, для элемента используется значение true.<br />
        ///   В противном случае — false.
        /// </summary>
        public bool? Pickup { get; set; }

        /// <summary>
        /// </summary>
        public double? LocalDeliveryCost { get; set; }

        /// <summary>
        ///   Описание товарного предложения
        /// </summary>
        public string Description { get; set; }

        /// <summary>
        ///   Элемент предназначен для отметки товаров, имеющих официальную гарантию производителя.
        /// </summary>
        public bool? ManufacturerWarranty { get; set; }

        /// <summary>
        ///   Элемент предназначен для указания страны производства товара.
        /// </summary>
        public string CountryOfOrigin { get; set; }

        /// <summary>
        /// </summary>
        public OrderingTime OrderingTime { get; set; }

        /// <summary>
        /// </summary>
        public string Aliases { get; set; }

        /// <summary>
        /// </summary>
        public List<string> Additionals { get; set; }

        ///<summary>
        ///  Элемент используется для отражения информации о минимальной сумме заказа, минимальной партии товара или необходимости предоплаты, <br />
        ///  а так же для описания акций магазина (кроме скидок).<br />
        ///  Допустимая длина текста в элементе - 50 символов.
        ///</summary>
        public string SalesNotes { get; set; }

        /// <summary>
        /// </summary>
        public string Promo { get; set; }

        /// <summary>
        ///   Элемент предназначен для обозначения товара, который можно скачать.
        /// </summary>
        public bool? Downloadable { get; set; }

        /// <summary>
        ///   Элемент обязателен для обозначения товара, имеющего отношение к удовлетворению сексуальных потребностей,<br /> 
        ///   либо иным образом эксплуатирующего интерес к сексу.
        /// </summary>
        public bool? Adult { get; set; }

        /// <summary>
        ///   Штрихкод товара, указанный производителем.
        /// </summary>
        public List<uint> Barcodes { get; set; }

        ///<summary>
        ///  Характеристики товара.
        ///  Для описания каждого параметра используется отдельный элемент param.
        ///</summary>
        public List<Param> Params { get; set; }

        #endregion

        internal static DummyOffer CreateDummyOfferFromXml(XPathNavigator nav)
        {
            DummyOffer offer = new DummyOffer();
            bool booleanVal;
            uint id;
            string strId = nav.GetAttribute("id", "");
            if(!string.IsNullOrEmpty(strId) && uint.TryParse(strId, out id))
                offer.ID = id;

            strId = nav.GetAttribute("available", "");
            if(!string.IsNullOrEmpty(strId) && Utils.TryParseYmlBoolean(strId, out booleanVal))
                offer.Available = booleanVal;

            strId = nav.GetAttribute("bid", "");
            if(!string.IsNullOrEmpty(strId) && uint.TryParse(strId, out id))
                offer.BID = id;

            strId = nav.GetAttribute("cbid", "");
            if(!string.IsNullOrEmpty(strId) && uint.TryParse(strId, out id))
                offer.CBID = id;

            XPathNavigator node = nav.SelectSingleNode("url");
            if(node != null)
                offer.Url = node.Value;

            node = nav.SelectSingleNode("buyurl");
            if(node != null)
                offer.BuyUrl = node.Value;

            double doubleVal;
            node = nav.SelectSingleNode("price");
            if(node != null && Utils.TryParseYmlDouble(node.Value, out doubleVal))
                offer.Price = doubleVal;

            node = nav.SelectSingleNode("wprice");
            if(node != null && Utils.TryParseYmlDouble(node.Value, out doubleVal))
                offer.Wprice = doubleVal;

            YmlCurrencyId currencyId;
            node = nav.SelectSingleNode("currencyId");
            if(node != null && Enum.TryParse(node.Value.Trim(), out currencyId))
                offer.CurrencyId = currencyId;

            node = nav.SelectSingleNode("xCategory");
            if(node != null)
                offer.Xcategory = node.Value;

            XPathNodeIterator iterator = nav.Select("categoryId");
            while(iterator.MoveNext())
            {
                if(iterator.Current != null)
                    offer.CategoryIds.Add(CategoryID.CreateCategoryIDFromXml(iterator.Current));
            }

            node = nav.SelectSingleNode("picture");
            if(node != null)
                offer.Picture = node.Value;

            node = nav.SelectSingleNode("store");
            if(node != null && Utils.TryParseYmlBoolean(node.Value, out booleanVal))
                offer.Store = booleanVal;

            node = nav.SelectSingleNode("pickup");
            if(node != null && Utils.TryParseYmlBoolean(node.Value, out booleanVal))
                offer.Pickup = booleanVal;

            node = nav.SelectSingleNode("delivery");
            if(node != null && Utils.TryParseYmlBoolean(node.Value, out booleanVal))
                offer.Delivery = booleanVal;

            node = nav.SelectSingleNode("deliveryIncluded");
            if(node != null && Utils.TryParseYmlBoolean(node.Value, out booleanVal))
                offer.DeliveryIncluded = booleanVal;

            node = nav.SelectSingleNode("local_delivery_cost");
            if(node != null && Utils.TryParseYmlDouble(node.Value, out doubleVal))
                offer.LocalDeliveryCost = doubleVal;

            node = nav.SelectSingleNode("orderingTime");
            if(node != null)
                offer.OrderingTime = OrderingTime.CreateOrderingTimeFromXml(node);

            node = nav.SelectSingleNode("aliases");
            if(node != null)
                offer.Aliases = node.Value;

            iterator = nav.Select("additional");
            if(iterator.Count > 0)
            {
                offer.Additionals = new List<string>();
                while(iterator.MoveNext())
                {
                    if(iterator.Current != null)
                        offer.Additionals.Add(iterator.Current.Value);
                }
            }

            node = nav.SelectSingleNode("description");
            if(node != null)
                offer.Description = node.Value;

            node = nav.SelectSingleNode("sales_notes");
            if(node != null)
                offer.SalesNotes = node.Value;

            node = nav.SelectSingleNode("promo");
            if(node != null)
                offer.Promo = node.Value;

            node = nav.SelectSingleNode("manufacturer_warranty");
            if(node != null && Utils.TryParseYmlBoolean(node.Value, out booleanVal))
                offer.ManufacturerWarranty = booleanVal;

            node = nav.SelectSingleNode("country_of_origin");
            if(node != null)
                offer.CountryOfOrigin = node.Value;

            node = nav.SelectSingleNode("downloadable");
            if(node != null && Utils.TryParseYmlBoolean(node.Value, out booleanVal))
                offer.Downloadable = booleanVal;

            node = nav.SelectSingleNode("adult");
            if(node != null && Utils.TryParseYmlBoolean(node.Value, out booleanVal))
                offer.Adult = booleanVal;

            iterator = nav.Select("barcode");
            if(iterator.Count > 0)
            {
                offer.Barcodes = new List<uint>();
                while(iterator.MoveNext())
                {
                    uint uintVal;
                    if(iterator.Current != null && uint.TryParse(iterator.Current.Value.Trim(), out uintVal))
                        offer.Barcodes.Add(uintVal);
                }
            }

            iterator = nav.Select("param");
            if(iterator.Count > 0)
            {
                offer.Params = new List<Param>();
                while(iterator.MoveNext())
                {
                    if(iterator.Current != null)
                        offer.Params.Add(Param.CreateParamFromXml(iterator.Current));
                }
            }

            return offer;
        }
    }
}